package messages

import (
	
	
	

	
	
	
	
	
	
	
	
	
	
	
	
	
)

// Reference: https://www.ietf.org/rfc/rfc4120.txt
// Section: 5.3

// Ticket implements the Kerberos ticket.
type Ticket struct {
	TktVNO           int                 `asn1:"explicit,tag:0"`
	Realm            string              `asn1:"generalstring,explicit,tag:1"`
	SName            types.PrincipalName `asn1:"explicit,tag:2"`
	EncPart          types.EncryptedData `asn1:"explicit,tag:3"`
	DecryptedEncPart EncTicketPart       `asn1:"optional"` // Not part of ASN1 bytes so marked as optional so unmarshalling works
}

// EncTicketPart is the encrypted part of the Ticket.
type EncTicketPart struct {
	Flags             asn1.BitString          `asn1:"explicit,tag:0"`
	Key               types.EncryptionKey     `asn1:"explicit,tag:1"`
	CRealm            string                  `asn1:"generalstring,explicit,tag:2"`
	CName             types.PrincipalName     `asn1:"explicit,tag:3"`
	Transited         TransitedEncoding       `asn1:"explicit,tag:4"`
	AuthTime          time.Time               `asn1:"generalized,explicit,tag:5"`
	StartTime         time.Time               `asn1:"generalized,explicit,optional,tag:6"`
	EndTime           time.Time               `asn1:"generalized,explicit,tag:7"`
	RenewTill         time.Time               `asn1:"generalized,explicit,optional,tag:8"`
	CAddr             types.HostAddresses     `asn1:"explicit,optional,tag:9"`
	AuthorizationData types.AuthorizationData `asn1:"explicit,optional,tag:10"`
}

// TransitedEncoding part of the ticket's encrypted part.
type TransitedEncoding struct {
	TRType   int32  `asn1:"explicit,tag:0"`
	Contents []byte `asn1:"explicit,tag:1"`
}

// NewTicket creates a new Ticket instance.
func ( types.PrincipalName,  string,  types.PrincipalName,  string,  asn1.BitString,  *keytab.Keytab,  int32,  int, , , ,  time.Time) (Ticket, types.EncryptionKey, error) {
	,  := crypto.GetEtype()
	if  != nil {
		return Ticket{}, types.EncryptionKey{}, krberror.Errorf(, krberror.EncryptingError, "error getting etype for new ticket")
	}
	,  := types.GenerateEncryptionKey()
	if  != nil {
		return Ticket{}, types.EncryptionKey{}, krberror.Errorf(, krberror.EncryptingError, "error generating session key")
	}

	 := EncTicketPart{
		Flags:     ,
		Key:       ,
		CRealm:    ,
		CName:     ,
		Transited: TransitedEncoding{},
		AuthTime:  ,
		StartTime: ,
		EndTime:   ,
		RenewTill: ,
	}
	,  := asn1.Marshal()
	if  != nil {
		return Ticket{}, types.EncryptionKey{}, krberror.Errorf(, krberror.EncodingError, "error marshalling ticket encpart")
	}
	 = asn1tools.AddASNAppTag(, asnAppTag.EncTicketPart)
	, ,  := .GetEncryptionKey(, , , )
	if  != nil {
		return Ticket{}, types.EncryptionKey{}, krberror.Errorf(, krberror.EncryptingError, "error getting encryption key for new ticket")
	}
	,  := crypto.GetEncryptedData(, , keyusage.KDC_REP_TICKET, )
	if  != nil {
		return Ticket{}, types.EncryptionKey{}, krberror.Errorf(, krberror.EncryptingError, "error encrypting ticket encpart")
	}
	 := Ticket{
		TktVNO:  iana.PVNO,
		Realm:   ,
		SName:   ,
		EncPart: ,
	}
	return , , nil
}

// Unmarshal bytes b into a Ticket struct.
func ( *Ticket) ( []byte) error {
	,  := asn1.UnmarshalWithParams(, , fmt.Sprintf("application,explicit,tag:%d", asnAppTag.Ticket))
	return 
}

// Marshal the Ticket.
func ( *Ticket) () ([]byte, error) {
	,  := asn1.Marshal(*)
	if  != nil {
		return nil, 
	}
	 = asn1tools.AddASNAppTag(, asnAppTag.Ticket)
	return , nil
}

// Unmarshal bytes b into the EncTicketPart struct.
func ( *EncTicketPart) ( []byte) error {
	,  := asn1.UnmarshalWithParams(, , fmt.Sprintf("application,explicit,tag:%d", asnAppTag.EncTicketPart))
	return 
}

// unmarshalTicket returns a ticket from the bytes provided.
func unmarshalTicket( []byte) ( Ticket,  error) {
	 = .Unmarshal()
	return
}

// UnmarshalTicketsSequence returns a slice of Tickets from a raw ASN1 value.
func unmarshalTicketsSequence( asn1.RawValue) ([]Ticket, error) {
	//This is a workaround to a asn1 decoding issue in golang - https://github.com/golang/go/issues/17321. It's not pretty I'm afraid
	//We pull out raw values from the larger raw value (that is actually the data of the sequence of raw values) and track our position moving along the data.
	 := .Bytes
	// Ignore the head of the asn1 stream (1 byte for tag and those for the length) as this is what tells us its a sequence but we're handling it ourselves
	 := 1 + asn1tools.GetNumberBytesInLengthHeader(.Bytes)
	var  []Ticket
	var  asn1.RawValue
	for  < (len()) {
		,  := asn1.UnmarshalWithParams([:], &, fmt.Sprintf("application,tag:%d", asnAppTag.Ticket))
		if  != nil {
			return nil, fmt.Errorf("unmarshaling sequence of tickets failed getting length of ticket: %v", )
		}
		,  := unmarshalTicket([:])
		if  != nil {
			return nil, fmt.Errorf("unmarshaling sequence of tickets failed: %v", )
		}
		 += len(.FullBytes)
		 = append(, )
	}
	MarshalTicketSequence()
	return , nil
}

// MarshalTicketSequence marshals a slice of Tickets returning an ASN1 raw value containing the ticket sequence.
func ( []Ticket) (asn1.RawValue, error) {
	 := asn1.RawValue{
		Class:      2,
		IsCompound: true,
	}
	if len() < 1 {
		// There are no tickets to marshal
		return , nil
	}
	var  []byte
	for ,  := range  {
		,  := .Marshal()
		if  != nil {
			return , fmt.Errorf("error marshaling ticket number %d in sequence of tickets", +1)
		}
		 = append(, ...)
	}
	// The ASN1 wrapping consists of 2 bytes:
	// 1st byte -> Identifier Octet - In this case an OCTET STRING (ASN TAG
	// 2nd byte -> The length (this will be the size indicated in the input bytes + 2 for the additional bytes we add here.
	// Application Tag:
	//| Byte:       | 8                            | 7                          | 6                                         | 5 | 4 | 3 | 2 | 1             |
	//| Value:      | 0                            | 1                          | 1                                         | From the RFC spec 4120        |
	//| Explanation | Defined by the ASN1 encoding rules for an application tag | A value of 1 indicates a constructed type | The ASN Application tag value |
	 = append(asn1tools.MarshalLengthBytes(len()), ...)
	 = append([]byte{byte(32 + asn1.TagSequence)}, ...)
	.Bytes = 
	// If we need to create the full bytes then identifier octet is "context-specific" = 128 + "constructed" + 32 + the wrapping explicit tag (11)
	//fmt.Fprintf(os.Stderr, "mRaw fb: %v\n", raw.FullBytes)
	return , nil
}

// DecryptEncPart decrypts the encrypted part of the ticket.
// The sname argument can be used to specify which service principal's key should be used to decrypt the ticket.
// If nil is passed as the sname then the service principal specified within the ticket it used.
func ( *Ticket) ( *keytab.Keytab,  *types.PrincipalName) error {
	if  == nil {
		 = &.SName
	}
	, ,  := .GetEncryptionKey(*, .Realm, .EncPart.KVNO, .EncPart.EType)
	if  != nil {
		return NewKRBError(.SName, .Realm, errorcode.KRB_AP_ERR_NOKEY, fmt.Sprintf("Could not get key from keytab: %v", ))
	}
	return .Decrypt()
}

// Decrypt decrypts the encrypted part of the ticket using the key provided.
func ( *Ticket) ( types.EncryptionKey) error {
	,  := crypto.DecryptEncPart(.EncPart, , keyusage.KDC_REP_TICKET)
	if  != nil {
		return fmt.Errorf("error decrypting Ticket EncPart: %v", )
	}
	var  EncTicketPart
	 = .Unmarshal()
	if  != nil {
		return fmt.Errorf("error unmarshaling encrypted part: %v", )
	}
	.DecryptedEncPart = 
	return nil
}

// GetPACType returns a Microsoft PAC that has been extracted from the ticket and processed.
func ( *Ticket) ( *keytab.Keytab,  *types.PrincipalName,  *log.Logger) (bool, pac.PACType, error) {
	var  bool
	for ,  := range .DecryptedEncPart.AuthorizationData {
		if .ADType == adtype.ADIfRelevant {
			var  types.AuthorizationData
			 := .Unmarshal(.ADData)
			if  != nil {
				.Printf("PAC authorization data could not be unmarshaled: %v", )
				continue
			}
			if [0].ADType == adtype.ADWin2KPAC {
				 = true
				var  pac.PACType
				 = .Unmarshal([0].ADData)
				if  != nil {
					return , , fmt.Errorf("error unmarshaling PAC: %v", )
				}
				if  == nil {
					 = &.SName
				}
				, ,  := .GetEncryptionKey(*, .Realm, .EncPart.KVNO, .EncPart.EType)
				if  != nil {
					return , , NewKRBError(.SName, .Realm, errorcode.KRB_AP_ERR_NOKEY, fmt.Sprintf("Could not get key from keytab: %v", ))
				}
				 = .ProcessPACInfoBuffers(, )
				return , , 
			}
		}
	}
	return , pac.PACType{}, nil
}

// Valid checks it the ticket is currently valid. Max duration passed endtime passed in as argument.
func ( *Ticket) ( time.Duration) (bool, error) {
	// Check for future tickets or invalid tickets
	 := time.Now().UTC()
	if .DecryptedEncPart.StartTime.Sub() >  || types.IsFlagSet(&.DecryptedEncPart.Flags, flags.Invalid) {
		return false, NewKRBError(.SName, .Realm, errorcode.KRB_AP_ERR_TKT_NYV, "service ticket provided is not yet valid")
	}

	// Check for expired ticket
	if .Sub(.DecryptedEncPart.EndTime) >  {
		return false, NewKRBError(.SName, .Realm, errorcode.KRB_AP_ERR_TKT_EXPIRED, "service ticket provided has expired")
	}

	return true, nil
}